package com.zsp.service;

import com.zsp.config.Constants;
import com.zsp.config.MyConfig;
import com.zsp.exception.FileNameLengthLimitExceededException;
import com.zsp.exception.FileSizeLimitExceededException;
import com.zsp.exception.InvalidExtensionException;
import com.zsp.utils.FileUploadUtils;
import com.zsp.utils.MimeTypeUtils;
import com.zsp.utils.StrFormatter;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public class FileUploadService {

    public String upload(MultipartFile file, String baseDir) throws IOException, FileSizeLimitExceededException, FileNameLengthLimitExceededException, InvalidExtensionException {

        // 1. 校验文件名长度
        if (file.getOriginalFilename().length() > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
        }

        // 2. 校验文件大小和扩展名
        FileUploadUtils.assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);

        // 3. 生成文件名，包含日期路径
        String fileName = generateFilename(file);

        // 4.获取绝对路径，本机地址路径 例如C:\xx\xxx\xxx.png
        String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();

        // 5.写文件
        file.transferTo(Paths.get(absPath));

        // 6.返回路径文件名 /profile/upload/2024/07/18/登录_20240718102038A001.png
        return getPathFileName(baseDir, fileName);
    }
    public static final String getPathFileName(String uploadDir, String fileName) throws IOException
    {
        int dirLastIndex = MyConfig.getProfile().length() + 1;
        String currentDir = substring(uploadDir, dirLastIndex);
        return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
    }

    /**
     * 截取字符串
     *
     * @param str 字符串
     * @param start 开始
     * @return 结果
     */
    public static String substring(final String str, int start)
    {
        if (str == null)
        {
            return NULLSTR;
        }

        if (start < 0)
        {
            start = str.length() + start;
        }

        if (start < 0)
        {
            start = 0;
        }
        if (start > str.length())
        {
            return NULLSTR;
        }

        return str.substring(start);
    }
    public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
    {
        File desc = new File(uploadDir + File.separator + fileName);

        if (!desc.exists())
        {
            if (!desc.getParentFile().exists())
            {
                desc.getParentFile().mkdirs();
            }
        }
        return desc;
    }
    // 上传序列类型
    public static final String uploadSeqType = "UPLOAD";
    // 生成文件名，包含日期路径
    private String generateFilename(MultipartFile file) {
        return format("{}/{}_{}.{}", datePath(),
                FilenameUtils.getBaseName(file.getOriginalFilename()),getId(uploadSeqType), getExtension(file));

    }
    /**
     * 获取文件名的后缀
     *
     * @param file 表单文件
     * @return 后缀名
     */
    public static final String getExtension(MultipartFile file)
    {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StringUtils.isEmpty(extension))
        {
            extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
        }
        return extension;
    }
    private static AtomicInteger commSeq = new AtomicInteger(1);
    // 上传接口序列数
    private static AtomicInteger uploadSeq = new AtomicInteger(1);

    public static String getId(String type)
    {
        AtomicInteger atomicInt = commSeq;
        if (uploadSeqType.equals(type))
        {
            atomicInt = uploadSeq;
        }
        return getId(atomicInt, 3);
    }
    // 机器标识
    private static final String machineCode = "A";
    public static String getId(AtomicInteger atomicInt, int length)
    {
        String result = dateTimeNow();
        result += machineCode;
        result += getSeq(atomicInt, length);
        return result;
    }
    private synchronized static String getSeq(AtomicInteger atomicInt, int length)
    {
        // 先取值再+1
        int value = atomicInt.getAndIncrement();

        // 如果更新后值>=10 的 (length)幂次方则重置为1
        int maxSeq = (int) Math.pow(10, length);
        if (atomicInt.get() >= maxSeq)
        {
            atomicInt.set(1);
        }
        // 转字符串，用0左补齐
        return padl(value, length);
    }
    public static final String padl(final Number num, final int size)
    {
        return padl(num.toString(), size, '0');
    }
    public static final String padl(final String s, final int size, final char c)
    {
        final StringBuilder sb = new StringBuilder(size);
        if (s != null)
        {
            final int len = s.length();
            if (s.length() <= size)
            {
                for (int i = size - len; i > 0; i--)
                {
                    sb.append(c);
                }
                sb.append(s);
            }
            else
            {
                return s.substring(len - size, len);
            }
        }
        else
        {
            for (int i = size; i > 0; i--)
            {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
    public static final String dateTimeNow()
    {
        return dateTimeNow(YYYYMMDDHHMMSS);
    }
    public static final String dateTimeNow(final String format)
    {
        return parseDateToStr(format, new Date());
    }
    public static final String parseDateToStr(final String format, final Date date)
    {
        return new SimpleDateFormat(format).format(date);
    }

    public static final String datePath()
    {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }
    public static String format(String template, Object... params)
    {
        if (isEmpty(params) || isEmpty(template))
        {
            return template;
        }
        return StrFormatter.format(template, params);
    }
    public static boolean isEmpty(Object[] objects)
    {
        return isNull(objects) || (objects.length == 0);
    }
    private static final String NULLSTR = "";
    public static boolean isEmpty(String str)
    {
        return isNull(str) || NULLSTR.equals(str.trim());
    }
    public static boolean isNull(Object object)
    {
        return object == null;
    }
}